#include <iostream>
#include <vector>
#include <windows.h>
#include <dbghelp.h>
#include <time.h>
#pragma comment(lib, "Dbghelp.lib")


typedef USHORT NTAPI RtlCaptureStackBackTrace_Function(
    IN ULONG frames_to_skip,
    IN ULONG frames_to_capture,
    OUT PVOID *backtrace,
    OUT PULONG backtrace_hash);

int get_stack_trace(void** buf, int max_depth = 99, int skip_cnt = 0)
{
    return RtlCaptureStackBackTrace(skip_cnt, max_depth, buf, 0);
}
//int get_stack_frames_with_context();
int get_stack_frames();
//int get_stack_trace_with_context();


int call_stack_test(int depth = 3)
{
    if(depth > 0)
    {
        --depth;
        // void* buf[100] = {0};
        // int ret = get_stack_trace(buf);
        // std::cout << 1 << std::endl;
        call_stack_test(depth);
    }
    else
    {
        const int kCallStackDepth = 100;
        void* buf[kCallStackDepth] = {0};
        int skip_frame_cnt = 1;
        int frames_cnt = get_stack_trace(buf, kCallStackDepth, skip_frame_cnt);
        HANDLE process = GetCurrentProcess();
        SymInitialize(process, NULL, TRUE);
        const int kMaxSymbolNameLen = 255;
        char symbol_buf[sizeof(SYMBOL_INFO) + kMaxSymbolNameLen * sizeof(TCHAR)] = {};
        PSYMBOL_INFO symbol_ptr = (PSYMBOL_INFO)symbol_buf;
        symbol_ptr->SizeOfStruct = sizeof(SYMBOL_INFO);
        symbol_ptr->MaxNameLen = MAX_SYM_NAME;
        IMAGEHLP_LINE64 line;
        line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
        DWORD64 displacementSym = 0;
        DWORD displacementLine = 0;
        for(int idx = 0; idx < frames_cnt; ++idx)
        {
            DWORD address = reinterpret_cast<DWORD>(buf[idx]);
            std::cout << std::hex << address << std::endl;
            //std::cout << (buf[idx]) << std::endl;
            if(SymFromAddr(process, address, &displacementSym, symbol_ptr) && 
                SymGetLineFromAddr64(process, address, &displacementLine, &line))
            {
                std::cout << symbol_ptr->Name << std::endl;
            }
        }
        return true;
    }

    return false;
}

// int printStackTrace()
// {
//     void *stack[TRACE_MAX_STACK_FRAMES];
//     HANDLE process = GetCurrentProcess();
//     SymInitialize(process, NULL, TRUE);
//     WORD numberOfFrames = CaptureStackBackTrace(0, TRACE_MAX_STACK_FRAMES, stack, NULL);
//     SYMBOL_INFO *symbol = (SYMBOL_INFO *)malloc(sizeof(SYMBOL_INFO)+(TRACE_MAX_FUNCTION_NAME_LENGTH - 1) * sizeof(TCHAR));
//     symbol->MaxNameLen = TRACE_MAX_FUNCTION_NAME_LENGTH;
//     symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
//     DWORD displacement;
//     IMAGEHLP_LINE64 *line = (IMAGEHLP_LINE64 *)malloc(sizeof(IMAGEHLP_LINE64));
//     line->SizeOfStruct = sizeof(IMAGEHLP_LINE64);
//     for (int i = 0; i < numberOfFrames; i++)
//     {
//         DWORD64 address = (DWORD64)(stack[i]);
//         SymFromAddr(process, address, NULL, symbol);
//         if (SymGetLineFromAddr64(process, address, &displacement, line))
//         {
//             printf("\tat %s in %s: line: %lu: address: 0x%0X\n", symbol->Name, line->FileName, line->LineNumber, symbol->Address);
//         }
//         else
//         {
//             printf("\tSymGetLineFromAddr64 returned error code %lu.\n", GetLastError());
//             printf("\tat %s, address 0x%0X.\n", symbol->Name, symbol->Address);
//         }
//     }
//     return 0;
// }

void call_stack_test1(int depth = 3)
{
    if(depth > 0)
    {
        --depth;
        // void* buf[100] = {0};
        // int ret = get_stack_trace(buf);
        // std::cout << 1 << std::endl;
        call_stack_test1(depth);
    }
    else
    {
        const int kCallStackDepth = 100;
        void* frames_buf[kCallStackDepth] = {0};
        get_stack_trace(frames_buf, kCallStackDepth);
    }
}

int main()
{
    int ret = call_stack_test();

    const int kCallStackDepth = 100;
    void* frames_buf[kCallStackDepth] = {0};
    int total_cnt = 1000000;
    time_t start_ts = time(NULL);
    for(int cnt = 0; cnt < total_cnt; ++cnt)
        call_stack_test1(100);
    time_t cost_sec = time(NULL) - start_ts;
    std::cout << "cost_sec=" << cost_sec << std::endl;
    return 0;
}